package cn.demoncat.util.lang;

import cn.demoncat.util.exception.BizRuntimeException;
import cn.demoncat.util.exception.ParamRuntimeException;
import cn.demoncat.util.lang.constant.DecimalConstant;
import cn.demoncat.util.lang.constant.StringConstant;
import org.apache.commons.lang3.StringUtils;

import java.math.BigDecimal;
import java.math.RoundingMode;
import java.util.Arrays;

/**
 * BigDecimal工具
 *
 * 注意：金额最大为15,2位（13位整数），否则转换字符串时错误
 * 
 * @author 延晓磊
 *
 * @since 2019年11月8日
 */
public class DecimalUtil {
	
	/**
	 * 转字符串：去掉末尾0，去掉科学计数法
	 * 
	 * @param o	默认0
	 * @return
	 * 
	 * @author 延晓磊
	 *
	 * @since 2020年7月10日
	 */
	public static String toString(BigDecimal o) {
		return trimZero(o).toPlainString();
	}

	/**
	 * 转换为字符串
	 *
	 * @param o
	 * @param def
	 * @return
	 *
	 * @author 延晓磊
	 * @since 2021年11月23日
	 */
	public static String toString(BigDecimal o, BigDecimal def){
		return o == null ? toString(def) : toString(o);
	}

	/**
	 * 去除末尾的0
	 *
	 * @param o
	 * @return
	 *
	 * @author 延晓磊
	 * @since 2021年11月23日
	 */
	public static BigDecimal trimZero(BigDecimal o){
		if (o == null) {
			return BigDecimal.ZERO;
		}
		return o.stripTrailingZeros();
	}

	/**
	 * 判断为空或0
	 * 
	 * @param o
	 * @return
	 * 
	 * @author 延晓磊
	 *
	 * @since 2019年1月16日
	 */
	public static boolean isEmpty(BigDecimal o) {
		return o == null || isEq(o);
	}

	/**
	 * 判断非空或0
	 *
	 * @param o
	 * @return
	 *
	 * @author 延晓磊
	 *
	 * @since 2019年1月16日
	 */
	public static boolean isNotEmpty(BigDecimal o) {
		return !isEmpty(o);
	}

	/**
	 * 判断为空或0时抛异常
	 *
	 * @param o
	 * @return
	 *
	 * @author 延晓磊
	 * @since 2022年03月25日
	 */
	public static BigDecimal checkEmpty(BigDecimal o){
		if (isEmpty(o)) {
			throw ParamRuntimeException.NULL;
		}
		return o;
	}

	/**
	 * null转0
	 * 
	 * @param o
	 * @return
	 * 
	 * @author 延晓磊
	 *
	 * @since 2018年6月5日
	 */
	public static BigDecimal toEmpty(BigDecimal o) {
		return o == null ? BigDecimal.ZERO : o;
	}

	/**
	 * 0转null
	 *
	 * @param o
	 * @return
	 *
	 * @author 延晓磊
	 *
	 * @since 2018年6月5日
	 */
	public static BigDecimal toNull(BigDecimal o){
		return BigDecimal.ZERO.equals(o) ? null : o;
	}

	/**
	 * null/0 转默认值
	 * 
	 * @param o		数据
	 * @param def	默认值
	 * @return
	 * 
	 * @author 延晓磊
	 *
	 * @since 2018年6月5日
	 */
	public static BigDecimal toDefault(BigDecimal o, BigDecimal def) {
		return isEmpty(o) ? def : o;
	}

	/**
	 * 转换字符串为BigDecimal
	 *
	 * @param num
	 * @param def	默认值
	 * @return 如果字符串为空则返回def，如果字符串非数值则异常
	 *
	 * @author 延晓磊
	 *
	 * @since 2017年1月5日
	 */
	public static BigDecimal valueOf(String num, BigDecimal def) {
		num = StringUtils.trimToNull(num);
		// 如果为空，返回def
		if (StringUtil.isBlank(num)) {
			return def;
		}
		// 非空转换，格式错误抛异常
		return new BigDecimal(num);
	}

	/**
	 * 转换字符串为BigDecimal
	 *
	 * @param num
	 * @return 如果字符串为空则返回null，如果字符串非数值则异常
	 *
	 * @author 延晓磊
	 *
	 * @since 2017年1月5日
	 */
	public static BigDecimal valueOf(String num) {
		return valueOf(num, null);
	}

	/**
	 * 转换字符串为BigDecimal
	 *
	 * @param num
	 * @return 如果字符串为空则返回0，如果字符串非数值则异常
	 *
	 * @author 延晓磊
	 *
	 * @since 2017年1月5日
	 */
	public static BigDecimal valueOfZero(String num) {
		return valueOf(num, BigDecimal.ZERO);
	}

	/**
	 * 保留size个小数位
	 * 
	 * @param num
	 * @param size
	 * @param mode	进位模式
	 * @return
	 * 
	 * @author 延晓磊
	 *
	 * @since 2019年5月31日
	 */
	public static BigDecimal toFixed(BigDecimal num, int size, RoundingMode mode) {
		return toEmpty(num).setScale(size, mode);
	}
	
	/**
	 * 四舍五入，保留size个小数位
	 * 
	 * @param num
	 * @param size
	 * @return
	 * 
	 * @author 延晓磊
	 *
	 * @since 2019年5月31日
	 */
	public static BigDecimal toFixed(BigDecimal num, int size) {
		return toFixed(num, size, RoundingMode.HALF_UP);
	}
	
	/**
	 * 向上进位，保留size个小数位
	 * 
	 * @param num
	 * @param size
	 * 
	 * @return 2.1 = 3; -2.1 = -3
	 * 
	 * @author 延晓磊
	 *
	 * @since 2020年1月9日
	 */
	public static BigDecimal toUp(BigDecimal num, int size) {
		return DecimalUtil.toFixed(num, size, RoundingMode.UP);
	}
	
	/**
	 * 向上进位，保留size个小数位
	 * 
	 * @param num
	 * @param size
	 * 
	 * @return 2.1 = 3; -2.1 = -2
	 * 
	 * @author 延晓磊
	 *
	 * @since 2020年1月9日
	 */
	public static BigDecimal toCeil(BigDecimal num, int size) {
		return DecimalUtil.toFixed(num, size, RoundingMode.CEILING);
	}
	
	/**
	 * 向下进位，保留size个小数位
	 * 
	 * @param num
	 * @param size
	 * 
	 * @return 2.9 = 2; -2.9 = -2
	 * 
	 * @author 延晓磊
	 *
	 * @since 2020年1月9日
	 */
	public static BigDecimal toDown(BigDecimal num, int size) {
		return DecimalUtil.toFixed(num, size, RoundingMode.DOWN);
	}
	
	/**
	 * 向下进位，保留size个小数位
	 * 
	 * @param num
	 * @param size
	 * 
	 * @return 2.9 = 2; -2.9 = -3
	 * 
	 * @author 延晓磊
	 *
	 * @since 2020年1月9日
	 */
	public static BigDecimal toFloor(BigDecimal num, int size) {
		return DecimalUtil.toFixed(num, size, RoundingMode.FLOOR);
	}

	/**
	 * 四舍五入，保留size个小数位
	 *
	 * @param num
	 * @param size
	 *
	 * @return 2.1 = 2; 2.5 = 3; -2.1 = 2; -2.5 = 3
	 *
	 * @author 延晓磊
	 *
	 * @since 2020年1月9日
	 */
	public static BigDecimal toRound(BigDecimal num, int size) {
		return DecimalUtil.toFixed(num, size, RoundingMode.HALF_UP);
	}

	/**
	 * 转换百分比
	 * 
	 * @param percent	小数：0.00
	 * 
	 * @return 整数（百分比）
	 * 
	 * @author 延晓磊
	 *
	 * @since 2020年3月10日
	 */
	public static int parsePercent(BigDecimal percent) {
		return toPercent(percent).intValue();
	}
	
	/**
	 * 转换百分比
	 * 
	 * @param percent	整数（百分比）
	 * 
	 * @return 小数：0.00
	 * 
	 * @author 延晓磊
	 *
	 * @since 2020年3月10日
	 */
	public static BigDecimal parsePercent(int percent) {
		return BigDecimal.valueOf(percent).divide(DecimalConstant.N100, 2, RoundingMode.FLOOR);
	}
	
	/**
	 * 转换百分比
	 * 
	 * @param ratio	比例：0.00
	 * 
	 * @return 百分比(乘100)
	 * 
	 * @author 延晓磊
	 *
	 * @since 2020年3月10日
	 */
	public static BigDecimal toPercent(BigDecimal ratio) {
		return ratio.multiply(DecimalConstant.N100);
	}

	/**
	 * 转换百分比
	 *
	 * @param ratio	比例：0.00
	 * @param len	小数位
	 *
	 * @return 百分比(乘100)
	 *
	 * @author 延晓磊
	 *
	 * @since 2020年3月10日
	 */
	public static BigDecimal toPercent(BigDecimal ratio, int len) {
		return ratio.multiply(DecimalConstant.N100).setScale(len, RoundingMode.HALF_UP);
	}

	/**
	 * 转换为百分比
	 *
	 * @param a
	 * @param b
	 * @param len	小数位
	 * @return
	 *
	 * @author 延晓磊
	 * @since 2022年06月23日
	 */
	public static BigDecimal toPercent(BigDecimal a, BigDecimal b, int len){
		return a.multiply(DecimalConstant.N100).divide(b, len, RoundingMode.HALF_UP);
	}

	/**
	 * 转换为百分比
	 *
	 * @param a
	 * @param b
	 * @param len	小数位
	 * @return
	 *
	 * @author 延晓磊
	 * @since 2022年06月23日
	 */
	public static BigDecimal toPercent(long a, long b, int len){
		return toPercent(BigDecimal.valueOf(a), BigDecimal.valueOf(b), len);
	}

	/**
	 * 转换为百分比
	 *
	 * @param a
	 * @param b
	 * @param len	小数位
	 * @return
	 *
	 * @author 延晓磊
	 * @since 2022年06月23日
	 */
	public static BigDecimal toPercent(double a, double b, int len){
		return toPercent(BigDecimal.valueOf(a), BigDecimal.valueOf(b), len);
	}

	/**
	 * 转换百分比
	 *
	 * @param ratio	比例：0.00%
	 *
	 * @return 百分比(乘100)
	 *
	 * @author 延晓磊
	 *
	 * @since 2020年3月10日
	 */
	public static String toPercentStr(BigDecimal ratio) {
		return ratio.multiply(DecimalConstant.N100) + StringConstant.PERCENT;
	}
	
	/**
	 * 比例计算 
	 * 
	 * @param n1	总数
	 * @param n2	子数
	 * @param size	小数位
	 * 
	 * @return 0.00(保留size位小数，舍弃余数)
	 * 
	 * @author 延晓磊
	 *
	 * @since 2019年5月31日
	 */
	public static BigDecimal toRatio(long n1, long n2, int size) {
		return BigDecimal.valueOf(n2).divide(BigDecimal.valueOf(n1), size, RoundingMode.FLOOR);
	}
	
	/**
	 * 比例计算 
	 * 
	 * @param n1	总数
	 * @param n2	子数
	 * 
	 * @return 0.00(保留2位小数，舍弃余数)
	 * 
	 * @author 延晓磊
	 *
	 * @since 2019年5月31日
	 */
	public static BigDecimal toRatio(long n1, long n2) {
		return toRatio(n1, n2, 2);
	}
	
	/**
	 * 比例计算 
	 * 
	 * @param n1	总数
	 * @param n2	子数
	 * @param size	小数位
	 * 
	 * @return 0.00(保留size位小数，舍弃余数)
	 * 
	 * @author 延晓磊
	 *
	 * @since 2019年5月31日
	 */
	public static BigDecimal toRatio(double n1, double n2, int size) {
		return BigDecimal.valueOf(n2).divide(BigDecimal.valueOf(n1), size, RoundingMode.FLOOR);
	}
	
	/**
	 * 比例计算 
	 * 
	 * @param n1	总数
	 * @param n2	子数
	 * 
	 * @return 0.00(保留2位小数，舍弃余数)
	 * 
	 * @author 延晓磊
	 *
	 * @since 2019年5月31日
	 */
	public static BigDecimal toRatio(double n1, double n2) {
		return toRatio(n1, n2, 2);
	}
	
	/**
	 * 比例计算 
	 * 
	 * @param n1	总数
	 * @param n2	子数
	 * @param size	小数位
	 * 
	 * @return 0.00(保留size位小数，舍弃余数)
	 * 
	 * @author 延晓磊
	 *
	 * @since 2019年5月31日
	 */
	public static BigDecimal toRatio(BigDecimal n1, BigDecimal n2, int size) {
		return n2.divide(n1, size, RoundingMode.FLOOR);
	}
	
	/**
	 * 比例计算 
	 * 
	 * @param n1	总数
	 * @param n2	子数
	 * 
	 * @return 0.00(保留2位小数，舍弃余数)
	 * 
	 * @author 延晓磊
	 *
	 * @since 2019年5月31日
	 */
	public static BigDecimal toRatio(BigDecimal n1, BigDecimal n2) {
		return toRatio(n1, n2, 2);
	}

	/**
	 * 运算：加 +
	 *
	 * @param vs	运算数
	 * @return
	 *
	 * @author 延晓磊
	 *
	 * @since 2019年11月11日
	 */
	public static BigDecimal add(BigDecimal ...vs) {
		if (vs == null) {
			return BigDecimal.ZERO;
		}
		if (vs.length == 1) {
			return vs[0];
		}
		if (vs.length == 2) {
			return vs[0].add(vs[1]);
		}
		return Arrays.stream(vs).reduce(BigDecimal::add).get();
	}

	/**
	 * 运算：减 -
	 *
	 * @param vs	运算数
	 * @return
	 *
	 * @author 延晓磊
	 *
	 * @since 2019年11月11日
	 */
	public static BigDecimal subtract(BigDecimal ...vs) {
		if (vs == null) {
			return BigDecimal.ZERO;
		}
		if (vs.length == 1) {
			return vs[0];
		}
		if (vs.length == 2) {
			return vs[0].subtract(vs[1]);
		}
		return Arrays.stream(vs).reduce(BigDecimal::subtract).get();
	}

	/**
	 * 运算：减 -
	 *
	 * @param vs	运算数
	 *
	 * @return 结果为负时返回0
	 *
	 * @author 延晓磊
	 *
	 * @since 2019年11月11日
	 */
	public static BigDecimal subtractZero(BigDecimal ...vs) {
		BigDecimal num = subtract(vs);
		if (num.compareTo(BigDecimal.ZERO) < 0) {
			return BigDecimal.ZERO;
		}
		return num;
	}

	/**
	 * 运算：减 -
	 *
	 * @param min	最小值，默认0
	 * @param msg	异常消息（结果小于min）
	 * @param vs	运算数
	 * @return
	 * 
	 * @author 延晓磊
	 *
	 * @since 2019年11月11日
	 */
	public static BigDecimal subtract(BigDecimal min, String msg, BigDecimal ...vs) {
		// 计算
		BigDecimal r = subtract(vs);
		// 比较
		if (min == null) {
			min = BigDecimal.ZERO;
		}
		if (r.compareTo(min) < 0) {
			if (msg == null) {
				msg = "计算结果不能小于" + toString(min);
			}
			throw new BizRuntimeException(msg);
		}
		return r;
	}
	
	/**
	 * 运算：减 -
	 *
	 * @param msg	异常消息（结果小于0）
	 * @param vs	运算数
	 * @return
	 * 
	 * @author 延晓磊
	 *
	 * @since 2019年11月11日
	 */
	public static BigDecimal subtractZero(String msg, BigDecimal ...vs) {
		return subtract(BigDecimal.ZERO, msg, vs);
	}
	
	/**
	 * 运算：乘 *
	 * 
	 * @param vs	运算数
	 * @return
	 * 
	 * @author 延晓磊
	 *
	 * @since 2019年11月11日
	 */
	public static BigDecimal multiply(BigDecimal ...vs) {
		if (vs == null) {
			return BigDecimal.ZERO;
		}
		if (vs.length == 1) {
			return vs[0];
		}
		if (vs.length == 2) {
			return vs[0].multiply(vs[1]);
		}
		return Arrays.stream(vs).reduce(BigDecimal::multiply).get();
	}

	/**
	 * 运算：除 /
	 *
	 * @param len	小数位数
	 * @param mode	进位模式
	 * @param vs	运算数：2个及以上，除数 == 0 = 被除数（不再抛异常 java.lang.ArithmeticException: / by zero）
	 * @return
	 * 
	 * @author 延晓磊
	 *
	 * @since 2019年11月11日
	 */
	public static BigDecimal divide(int len, RoundingMode mode, BigDecimal ...vs) {
		return Arrays.stream(vs).reduce((a,b) -> {
			if (isEmpty(b)) {
				return a.setScale(len, mode);
			}else{
				return a.divide(b, len, mode);
			}
		}).get();
	}

	/**
	 * 运算：取模
	 * 
	 * @param v1
	 * @param v2
	 * 
	 * @return 余
	 * 
	 * @author 延晓磊
	 *
	 * @since 2019年11月11日
	 */
	public static BigDecimal mod(BigDecimal v1, BigDecimal v2) {
		return v1.remainder(v2);
	}
	
	/**
	 * 运算：取模
	 * 
	 * @param v1
	 * @param v2
	 * @param len	小数位数（商）
	 * @param mode	进位模式（商）
	 * 
	 * @return 余
	 * 
	 * @author 延晓磊
	 *
	 * @since 2019年11月11日
	 */
	public static BigDecimal mod(BigDecimal v1, BigDecimal v2, int len, RoundingMode mode) {
		return divideMod(v1, v2, len, mode)[1];
	}
	
	/**
	 * 运算：求商取模
	 * 
	 * @param v1
	 * @param v2
	 * @return [商(整数), 余]
	 * 
	 * @author 延晓磊
	 *
	 * @since 2019年11月11日
	 */
	public static BigDecimal[] divideMod(BigDecimal v1, BigDecimal v2) {
		return v1.divideAndRemainder(v2);
	}
	
	/**
	 * 运算：求商取模
	 * 
	 * @param v1
	 * @param v2
	 * @param len	小数位数（商）
	 * @param mode	进位模式（商）
	 * 
	 * @return [商, 余]
	 * 
	 * @author 延晓磊
	 *
	 * @since 2019年11月11日
	 */
	public static BigDecimal[] divideMod(BigDecimal v1, BigDecimal v2, int len, RoundingMode mode) {
		BigDecimal[] result = new BigDecimal[2];
        result[0] = divide(len, mode, v1, v2);
        result[1] = v1.subtract(result[0].multiply(v2));
		return result;
	}
	
	/**
	 * 大于
	 * 
	 * @param v1
	 * @param v2
	 * @return
	 * 
	 * @author 延晓磊
	 *
	 * @since 2020年2月5日
	 */
	public static boolean isGt(BigDecimal v1, BigDecimal v2) {
		return v1.compareTo(v2) > 0;
	}
	
	/**
	 * 大于等于
	 * 
	 * @param v1
	 * @param v2
	 * @return
	 * 
	 * @author 延晓磊
	 *
	 * @since 2020年2月5日
	 */
	public static boolean isGe(BigDecimal v1, BigDecimal v2) {
		return v1.compareTo(v2) >= 0;
	}
	
	/**
	 * 小于
	 * 
	 * @param v1
	 * @param v2
	 * @return
	 * 
	 * @author 延晓磊
	 *
	 * @since 2020年2月5日
	 */
	public static boolean isLt(BigDecimal v1, BigDecimal v2) {
		return v1.compareTo(v2) < 0;
	}
	
	/**
	 * 小于等于
	 * 
	 * @param v1
	 * @param v2
	 * @return
	 * 
	 * @author 延晓磊
	 *
	 * @since 2020年2月5日
	 */
	public static boolean isLe(BigDecimal v1, BigDecimal v2) {
		return v1.compareTo(v2) <= 0;
	}
	
	/**
	 * 等于
	 * 
	 * @param v1
	 * @param v2
	 * @return
	 * 
	 * @author 延晓磊
	 *
	 * @since 2020年2月5日
	 */
	public static boolean isEq(BigDecimal v1, BigDecimal v2) {
		return v1.compareTo(v2) == 0;
	}
	
	/**
	 * 不等于
	 * 
	 * @param v1
	 * @param v2
	 * @return
	 * 
	 * @author 延晓磊
	 *
	 * @since 2020年2月5日
	 */
	public static boolean isNe(BigDecimal v1, BigDecimal v2) {
		return v1.compareTo(v2) != 0;
	}
	
	/**
	 * 大于0
	 * 
	 * @param v1
	 * @return
	 * 
	 * @author 延晓磊
	 *
	 * @since 2020年2月5日
	 */
	public static boolean isGt(BigDecimal v1) {
		return v1.compareTo(BigDecimal.ZERO) > 0;
	}
	
	/**
	 * 大于等于0
	 * 
	 * @param v1
	 * @return
	 * 
	 * @author 延晓磊
	 *
	 * @since 2020年2月5日
	 */
	public static boolean isGe(BigDecimal v1) {
		return v1.compareTo(BigDecimal.ZERO) >= 0;
	}
	
	/**
	 * 小于0
	 * 
	 * @param v1
	 * @return
	 * 
	 * @author 延晓磊
	 *
	 * @since 2020年2月5日
	 */
	public static boolean isLt(BigDecimal v1) {
		return v1.compareTo(BigDecimal.ZERO) < 0;
	}
	
	/**
	 * 小于等于0
	 * 
	 * @param v1
	 * @return
	 * 
	 * @author 延晓磊
	 *
	 * @since 2020年2月5日
	 */
	public static boolean isLe(BigDecimal v1) {
		return v1.compareTo(BigDecimal.ZERO) <= 0;
	}
	
	/**
	 * 等于0
	 * 
	 * @param v1
	 * @return
	 * 
	 * @author 延晓磊
	 *
	 * @since 2020年2月5日
	 */
	public static boolean isEq(BigDecimal v1) {
		return v1.compareTo(BigDecimal.ZERO) == 0;
	}
	
	/**
	 * 不等于0
	 * 
	 * @param v1
	 * @return
	 * 
	 * @author 延晓磊
	 *
	 * @since 2020年2月5日
	 */
	public static boolean isNe(BigDecimal v1) {
		return v1.compareTo(BigDecimal.ZERO) != 0;
	}

	/**
	 * 判断区间 min <= v <= max
	 *
	 * @param v
	 * @param min
	 * @param max
	 * @return
	 *
	 * @author 延晓磊
	 *
	 * @since 2020年2月5日
	 */
	public static boolean isRange(BigDecimal v, BigDecimal min, BigDecimal max) {
		return v.compareTo(min) >= 0 && v.compareTo(max) <= 0;
	}

	/**
	 * 限定区间 min <= v <= max
	 *
	 * @param v
	 * @param min
	 * @param max
	 * @return
	 *
	 * @author 延晓磊
	 *
	 * @since 2020年2月5日
	 */
	public static BigDecimal range(BigDecimal v, BigDecimal min, BigDecimal max) {
		if(v.compareTo(min) < 0){
			return min;
		}else if (v.compareTo(max) > 0) {
			return max;
		}else{
			return v;
		}

	}

	/**
	 * 判断区间 0 <= v <= max
	 *
	 * @param v
	 * @param max
	 * @return
	 *
	 * @author 延晓磊
	 *
	 * @since 2020年2月5日
	 */
	public static boolean isRange(BigDecimal v, BigDecimal max) {
		return isRange(v, BigDecimal.ZERO, max);
	}

	/**
	 * 获取区间 0 <= v <= max
	 *
	 * @param v
	 * @param max
	 * @return
	 *
	 * @author 延晓磊
	 *
	 * @since 2020年2月5日
	 */
	public static BigDecimal range(BigDecimal v, BigDecimal max) {
		return range(v, BigDecimal.ZERO, max);
	}
	
	/**
	 * 获取相反数
	 * 
	 * @param v
	 * @return -v
	 * 
	 * @author 延晓磊
	 *
	 * @since 2020年2月6日
	 */
	public static BigDecimal negate(BigDecimal v) {
		if (isEmpty(v)) {
			return v;
		}
		return v.negate();
	}


}
